﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Storage;

namespace BenevolentSunData
{
    public class Map : ICloneable
    {
        #region Layout

        public uint Par;

        public uint Seconds;

        public Point MapDimensions;

        // cells[col, row]    ie [x, y]   ie[i, j]
        [ContentSerializerIgnore]
        private Cell[,] cells;
        [ContentSerializerIgnore]
        public Cell[,] Cells
        {
            get { return cells; }
        }

        public Cell GetCell(Point coords)
        {
            return GetCell(coords.X, coords.Y);
        }

        public Cell GetCell(int x, int y)
        {
            if (x < 0 || y < 0 || x >= MapDimensions.X || y >= MapDimensions.Y)
                return null;
            return Cells[x, y];
        }

        public void SetCell(Cell cell)
        {
            Cell oldCell = Cells[cell.Coords.X, cell.Coords.Y];
            Cells[cell.Coords.X, cell.Coords.Y] = cell;

            if (oldCell != null)
            {
                if (oldCell is EmptyCell)
                    EmptyCells.Remove(oldCell as EmptyCell);
                else if (oldCell is HueCell)
                    HueCells.Remove(oldCell as HueCell);
                else if (oldCell is MirrorCell)
                    MirrorCells.Remove(oldCell as MirrorCell);
                else if (oldCell is NullCell)
                    NullCells.Remove(oldCell as NullCell);
                else if (oldCell is PrismCell)
                    PrismCells.Remove(oldCell as PrismCell);
                else if (oldCell is SinkCell)
                    SinkCells.Remove(oldCell as SinkCell);
                else if (oldCell is SourceCell)
                    SourceCells.Remove(oldCell as SourceCell);
                else if (oldCell is BulbCell)
                    BulbCells.Remove(oldCell as BulbCell);
                else if (oldCell is WallCell)
                    WallCells.Remove(oldCell as WallCell);
                else
                    throw (new Exception("Can't remove old cell, type not found: " + oldCell));
            }

            if (cell is EmptyCell)
                EmptyCells.Add(cell as EmptyCell);
            else if (cell is HueCell)
                HueCells.Add(cell as HueCell);
            else if (cell is MirrorCell)
                MirrorCells.Add(cell as MirrorCell);
            else if (cell is NullCell)
                NullCells.Add(cell as NullCell);
            else if (cell is PrismCell)
                PrismCells.Add(cell as PrismCell);
            else if (cell is SinkCell)
                SinkCells.Add(cell as SinkCell);
            else if (cell is SourceCell)
                SourceCells.Add(cell as SourceCell);
            else if (cell is BulbCell)
                BulbCells.Add(cell as BulbCell);
            else if (cell is WallCell)
                WallCells.Add(cell as WallCell);
            else
                throw (new Exception("Can't add new cell, type not found: " + cell));
        }

        public Cell GetNextCell(Cell cell, Direction direction)
        {
            switch (direction)
            {
                case Direction.North:
                    return GetCell(cell.Coords.X, cell.Coords.Y - 1);
                case Direction.South:
                    return GetCell(cell.Coords.X, cell.Coords.Y + 1);
                case Direction.East:
                    return GetCell(cell.Coords.X + 1, cell.Coords.Y);
                case Direction.West:
                    return GetCell(cell.Coords.X - 1, cell.Coords.Y);
                default:
                    throw (new Exception("Unknown direction: " + direction));
            }
        }
        #endregion


        #region Audio
        [ContentSerializer(Optional=true)]
        public String AmbientCueName = "";

        [ContentSerializer(Optional = true)]
        public String MusicCueName = "";
        #endregion


        #region Graphics
        [ContentSerializerIgnore]
        public Effect SwitchEffect;

        [ContentSerializerIgnore]
        public bool ShaderModel3;

        [ContentSerializerIgnore]
        public Fog Fog;

        [ContentSerializerIgnore]
        public AmbientLight AmbientLight;

        [ContentSerializerIgnore]
        public List<PointLight> PointLights = new List<PointLight>();

        [ContentSerializerIgnore]
        public List<BeamLight> BeamLights = new List<BeamLight>();

        [ContentSerializerIgnore]
        public List<BeamSegment> Segments = new List<BeamSegment>();

        public String MapSkinName;

        [ContentSerializerIgnore]
        public MapSkin Skin;

        [ContentSerializer(Optional=true)]
        public Vector3 FogColor = Color.Black.ToVector3();

        [ContentSerializer(Optional = true)]
        public float FogStart = 650f;

        [ContentSerializer(Optional = true)]
        public float FogEnd = 1000f;

        [ContentSerializer(Optional = true)]
        public bool FogEnabled = false;
        #endregion


        #region Empty Cells
        public List<EmptyCell> EmptyCells = new List<EmptyCell>();
        #endregion


        #region Wall Cells
        public List<WallCell> WallCells;
        #endregion


        #region Source Cells
        public List<SourceCell> SourceCells;
        #endregion


        #region Sink Cells
        public List<SinkCell> SinkCells;
        #endregion


        #region Mirror Cells
        public List<MirrorCell> MirrorCells;
        #endregion


        #region Prism Cells
        public List<PrismCell> PrismCells;
        #endregion


        #region Hue Cells
        public List<HueCell> HueCells;
        #endregion


        #region Torch Cells
        public List<BulbCell> BulbCells;
        #endregion


        #region Null Cells
        [ContentSerializerIgnore]
        public List<NullCell> NullCells;
        #endregion

        
        #region Beams
        List<Traveller> travellers = new List<Traveller>();

        public void ClearBeams()
        {
            foreach (Cell cell in cells)
            {
                cell.Clear();
            }

            BeamLights.Clear();
        }

        public void InitBeams()
        {
            // Clear old beams
            ClearBeams();

            // Travel to find all the beams
            foreach (SourceCell source in SourceCells)
            {
                Traveller t = new Traveller();
                t.lastCell = source;
                t.direction = source.CreateBeam();
                t.beam = source.GetBeam(t.direction);

                travellers.Add(t);
            }
            while (travellers.Count > 0)
                Travel();


            // Cache the newly found segments
            List<BeamSegment> segments = new List<BeamSegment>();
            BeamSegment openSegment = null;

            // Compile the segments - along x axis
            for (int y = 0; y < MapDimensions.Y; y++)
            {
                for (int x = 0; x < MapDimensions.X; x++)
                {
                    Cell cell = Cells[x, y];
                    

                    if (openSegment == null)
                    {
                        if (cell is BulbCell)
                            continue;
                        if (cell.GetBeam(Direction.West) != null)
                        {
                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.West).Color;
                            openSegment.Start = new Vector3(cell.Coords.X - 0.5f, 0.5f, cell.Coords.Y) * 39f;
                            Console.WriteLine("opening w at: " + cell.Coords);

                            if (cell.GetBeam(Direction.East) == null)
                            {
                                openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                segments.Add(openSegment);
                                openSegment = null;
                                Console.WriteLine("closing e at: " + cell.Coords);
                            }
                            else if (cell.GetBeam(Direction.West).Equals(cell.GetBeam(Direction.East)) == false)
                            {
                                openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                segments.Add(openSegment);
                                openSegment = null;
                                Console.WriteLine("closing e at: " + cell.Coords);

                                openSegment = new BeamSegment();
                                openSegment.Color = cell.GetBeam(Direction.East).Color;
                                openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                Console.WriteLine("opening w at: " + cell.Coords);
                            }
                        }
                        else if (cell.GetBeam(Direction.East) != null)
                        {
                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.East).Color;
                            openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            Console.WriteLine("opening e at: " + cell.Coords);
                        }
                    }
                    else
                    {
                        if (cell.GetBeam(Direction.West) == null)
                        {
                            openSegment.End = new Vector3(cell.Coords.X-0.5f, 0.5f, cell.Coords.Y) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing w at: " + cell.Coords);
                        }
                        else if (cell.GetBeam(Direction.East) == null)
                        {
                            openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing e at: " + cell.Coords);
                        }
                        else if (cell.GetBeam(Direction.West).Equals(cell.GetBeam(Direction.East)) == false)
                        {
                            openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing e at: " + cell.Coords);

                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.East).Color;
                            openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            Console.WriteLine("opening w at: " + cell.Coords);
                        } 
                    }
                }
            }
            
            // Find segments - y axis
            for (int x = 0; x < MapDimensions.X; x++)
            {
                for (int y = 0; y < MapDimensions.Y; y++)
                {
                    Cell cell = Cells[x, y];


                    if (openSegment == null)
                    {
                        if (cell is BulbCell)
                            continue;
                        if (cell.GetBeam(Direction.North) != null)
                        {
                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.North).Color;
                            openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y - 0.5f) * 39f;
                            Console.WriteLine("opening n at: " + cell.Coords);

                            if (cell.GetBeam(Direction.South) == null)
                            {
                                openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                segments.Add(openSegment);
                                openSegment = null;
                                Console.WriteLine("closing e at: " + cell.Coords);
                            }
                            else if (cell.GetBeam(Direction.North).Equals(cell.GetBeam(Direction.South)) == false)
                            {
                                openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                segments.Add(openSegment);
                                openSegment = null;
                                Console.WriteLine("closing n at: " + cell.Coords);

                                openSegment = new BeamSegment();
                                openSegment.Color = cell.GetBeam(Direction.South).Color;
                                openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                                Console.WriteLine("opening e at: " + cell.Coords);
                            }
                        }
                        else if (cell.GetBeam(Direction.South) != null)
                        {
                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.South).Color;
                            openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            Console.WriteLine("opening e at: " + cell.Coords);
                        }
                    }
                    else
                    {
                        if (cell.GetBeam(Direction.North) == null)
                        {
                            openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y - 0.5f) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing w at: " + cell.Coords);
                        }
                        else if (cell.GetBeam(Direction.South) == null)
                        {
                            openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing e at: " + cell.Coords);
                        }
                        else if (cell.GetBeam(Direction.North).Equals(cell.GetBeam(Direction.South)) == false)
                        {
                            openSegment.End = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            segments.Add(openSegment);
                            openSegment = null;
                            Console.WriteLine("closing e at: " + cell.Coords);

                            openSegment = new BeamSegment();
                            openSegment.Color = cell.GetBeam(Direction.South).Color;
                            openSegment.Start = new Vector3(cell.Coords.X, 0.5f, cell.Coords.Y) * 39f;
                            Console.WriteLine("opening w at: " + cell.Coords);
                        }
                    }
                }
            }

            // Add the entrance and exit segments
            foreach (SourceCell sourceCell in SourceCells)
            {
                openSegment = new BeamSegment();
                openSegment.End = new Vector3(sourceCell.Coords.X, 0.5f, sourceCell.Coords.Y) * 39f;
                openSegment.Start = new Vector3(sourceCell.Coords.X, -425f / 39f, sourceCell.Coords.Y) * 39f;
                openSegment.Color = sourceCell.BeamColor;
                //openSegment.IsEntrance = true;
                segments.Add(openSegment);

                openSegment = new BeamSegment();
                openSegment.End = new Vector3(sourceCell.Coords.X, -425f / 39f, sourceCell.Coords.Y) * 39f;
                openSegment.Start = new Vector3(sourceCell.Coords.X, -650f / 39f, sourceCell.Coords.Y) * 39f;
                openSegment.Color = sourceCell.BeamColor;
                openSegment.IsEntrance = true;
                segments.Add(openSegment);
            }
            foreach (SinkCell sinkCell in SinkCells)
            {
                if (sinkCell.IsActivated)
                {
                    openSegment = new BeamSegment();
                    openSegment.Start = new Vector3(sinkCell.Coords.X, 0.5f, sinkCell.Coords.Y) * 39f;
                    openSegment.End = new Vector3(sinkCell.Coords.X, 25.64f, sinkCell.Coords.Y) * 39f;
                    openSegment.Color = sinkCell.RequiredColor;
                    openSegment.IsExit = true;
                    segments.Add(openSegment);
                }
            }

            // Keep track of the segments
            Segments = segments;

            // Create beam lights for all the segments
            foreach (BeamSegment segment in segments)
            {
                BeamLight beamLight = new BeamLight(SwitchEffect);
                beamLight.Color = new Color(segment.Color.ToVector3() * 0.65f);
                beamLight.StartPosition = segment.Start;
                beamLight.EndPosition = segment.End;
                beamLight.Range = 39f * 2f;
                BeamLights.Add(beamLight);

                VertexPositionColorTexture[] beamVerts = new VertexPositionColorTexture[4];
                Vector3 right = new Vector3(6.5f, 0, 0);
                Vector3 up = new Vector3(0, 0, 1f);
                // Upper left corner 
                beamVerts[0].Position.X = -right.X + up.X;
                beamVerts[0].Position.Y = -right.Y + up.Y;
                beamVerts[0].Position.Z = -right.Z + up.Z;
                beamVerts[0].TextureCoordinate.X = 0;
                beamVerts[0].TextureCoordinate.Y = 1;
                // Lower left corner 
                beamVerts[1].Position.X = -right.X;
                beamVerts[1].Position.Y = -right.Y;
                beamVerts[1].Position.Z = -right.Z;
                beamVerts[1].TextureCoordinate.X = 0;
                beamVerts[1].TextureCoordinate.Y = 0;
                // Lower right corner 
                beamVerts[2].Position.X = right.X;
                beamVerts[2].Position.Y = right.Y;
                beamVerts[2].Position.Z = right.Z;
                beamVerts[2].TextureCoordinate.X = 1;
                beamVerts[2].TextureCoordinate.Y = 0;
                // Upper right corner 
                beamVerts[3].Position.X = right.X + up.X;
                beamVerts[3].Position.Y = right.Y + up.Y;
                beamVerts[3].Position.Z = right.Z + up.Z;
                beamVerts[3].TextureCoordinate.X = 1;
                beamVerts[3].TextureCoordinate.Y = 1;

                beamVerts[0].Color =
                beamVerts[1].Color =
                beamVerts[2].Color =
                beamVerts[3].Color = segment.Color;
                if (segment.IsEntrance)
                    beamVerts[1].Color.A = beamVerts[2].Color.A = 0;

                segment.VertexBuffer = beamVerts;
            }
        }

        public class BeamSegment
        {
            public Color Color;
            public Vector3 Start;
            public Vector3 End;
            public bool IsEntrance = false;
            public bool IsExit = false;
            public Matrix World = Matrix.Identity;
            public bool Initialized = false;
            public VertexPositionColorTexture[] VertexBuffer = new VertexPositionColorTexture[4];
        }
        
        private void Travel()
        {
            List<Traveller> travelling = travellers;
            travellers = new List<Traveller>();
            //Console.WriteLine("travelling");
            foreach (Traveller t in travelling)
            {
                Cell nextCell = GetNextCell(t.lastCell, t.direction);
                if (nextCell != null)
                {
                    //Console.WriteLine("Explored: " + nextCell + " :: " + nextCell.Coords);
                    List<Direction> output = nextCell.AddInput(t.beam, DirectionUtils.Invert(t.direction));
                    foreach (Direction next in output)
                    {
                        Traveller newT = new Traveller();
                        newT.beam = nextCell.GetBeam(next);
                        newT.lastCell = nextCell;
                        newT.direction = next;
                        travellers.Add(newT);
                    }
                }
            }
        }

        class Traveller
        {
            public Beam beam;
            public Cell lastCell;
            public Direction direction;
        }
        #endregion


        #region Initialization
        public void LoadContent(ContentManager content, GraphicsDevice graphicsDevice)
        {
            Skin = content.Load<MapSkin>(MapSkinName);
            foreach (Material mat in Skin.Materials.Values)
                mat.LoadContent(content, graphicsDevice);

            // initialize cell array to null
            cells = new Cell[MapDimensions.X, MapDimensions.Y];
            for (int j = 0; j < MapDimensions.Y; ++j)
            {
                for (int i = 0; i < MapDimensions.X; ++i)
                {
                    cells[i, j] = null;
                }
            }


            SwitchEffect = null;
            if ((graphicsDevice.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 3) && !Settings.ForceShaderModel_2_0)
            {
                SwitchEffect = content.Load<Effect>("Effects/SwitchEffect_3_0");
                ShaderModel3 = true;
            }
            else
            {
                SwitchEffect = content.Load<Effect>("Effects/SwitchEffect_2_0");
                ShaderModel3 = false;
            }
            Fog = new Fog(SwitchEffect);
            Fog.Color = new Color(FogColor);
            Fog.End = FogEnd;
            Fog.Start = FogStart;
            Fog.Enabled = FogEnabled;

            // add in empties
            foreach (EmptyCell empty in EmptyCells)
            {
                empty.LoadContent(content, this);
                cells[empty.Coords.X, empty.Coords.Y] = empty;
            }

            // add in walls
            foreach (WallCell wall in WallCells)
            {
                wall.LoadContent(content, this);
                cells[wall.Coords.X, wall.Coords.Y] = wall;
            }

            // add in sources
            foreach (SourceCell source in SourceCells)
            {
                source.LoadContent(content, this);
                if (cells[source.Coords.X, source.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + source + " position " + source.Coords + " is already taken by: " + cells[source.Coords.X, source.Coords.Y]));
                cells[source.Coords.X, source.Coords.Y] = source;
            }

            // add in sinks
            foreach (SinkCell sink in SinkCells)
            {
                sink.LoadContent(content, this);
                if (cells[sink.Coords.X, sink.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + sink + " position " + sink.Coords + " is already taken by: " + cells[sink.Coords.X, sink.Coords.Y]));
                cells[sink.Coords.X, sink.Coords.Y] = sink;
            }

            // add in mirrors
            foreach (MirrorCell mirror in MirrorCells)
            {
                mirror.LoadContent(content, this);
                if (cells[mirror.Coords.X, mirror.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + mirror + " position " + mirror.Coords + " is already taken by: " + cells[mirror.Coords.X, mirror.Coords.Y]));
                cells[mirror.Coords.X, mirror.Coords.Y] = mirror;
            }

            // add in prisms
            foreach (PrismCell prism in PrismCells)
            {
                prism.LoadContent(content, this);
                if (cells[prism.Coords.X, prism.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + prism + " position " + prism.Coords + " is already taken by: " + cells[prism.Coords.X, prism.Coords.Y]));
                cells[prism.Coords.X, prism.Coords.Y] = prism;
            }

            // add in hues
            foreach (HueCell hue in HueCells)
            {
                hue.LoadContent(content, this);
                if (cells[hue.Coords.X, hue.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + hue + " position " + hue.Coords + " is already taken by: " + cells[hue.Coords.X, hue.Coords.Y]));
                cells[hue.Coords.X, hue.Coords.Y] = hue;
            }

            // add in torches
            foreach (BulbCell torch in BulbCells)
            {
                torch.LoadContent(content, this);
                if (cells[torch.Coords.X, torch.Coords.Y] != null)
                    throw (new Exception("Cannot add: " + torch + " position " + torch.Coords + " is already taken by: " + cells[torch.Coords.X, torch.Coords.Y]));
                cells[torch.Coords.X, torch.Coords.Y] = torch;
            }

            // back fill with null cells and border walls
            NullCells = new List<NullCell>();
            BackfillNullCells();
        }

        private void BackfillNullCells()
        {

            for (int j = 0; j < MapDimensions.Y; ++j)
            {
                for (int i = 0; i < MapDimensions.X; ++i)
                {
                    if (cells[i, j] != null)
                        continue;

                    NullCell cell = new NullCell();
                    cell.Coords = new Point(i, j);
                    NullCells.Add(cell);
                    cells[i, j] = cell;

                    /*
                    if (j == 0 || j == map.MapDimensions.Y - 1 || i == 0 || i == map.MapDimensions.X - 1)
                    {
                        WallCell wall = new WallCell();
                        wall.Coords.X = i;
                        wall.Coords.Y = j;
                        wall.Model = input.ContentManager.Load<Model>(wall.ModelName);
                        map.WallCells.Add(wall);
                        map.cells[i, j] = wall;
                    }
                    else
                    {*/

                    //}
                }
            }
        }

        private void BuildBorderWalls(ContentManager Content)
        {
            for (int j = 0; j < MapDimensions.Y; ++j)
            {
                for (int i = 0; i < MapDimensions.X; ++i)
                {
                    if ((cells[i, j] is NullCell) == false)
                        continue;

                    bool shouldBeWall = false;
                    for (int i2 = i - 1; i2 <= i + 1; ++i2)
                    {
                        if (i2 < 0 || i2 >= MapDimensions.X)
                            continue;
                        for (int j2 = j - 1; j2 <= j + 1; ++j2)
                        {
                            if (j2 < 0 || j2 >= MapDimensions.Y)
                                continue;
                            if (j2 == j && i2 == i)
                                continue;
                            Cell cell = cells[i2, j2];
                            if (cell is WallCell == false && cell is NullCell == false)
                            {
                                shouldBeWall = true;
                                break;
                            }
                        }
                        if (shouldBeWall)
                            break;
                    }

                    if (shouldBeWall)
                    {
                        WallCell wall = new WallCell();
                        wall.Coords = new Point(i, j);
                        wall.LoadContent(Content, this);
                        WallCells.Add(wall);
                        cells[i, j] = wall;
                    }
                }
            }
        }
        #endregion


        #region ICloneable
        public object Clone()
        {
            Map map = new Map();

            map.MapDimensions = new Point(MapDimensions.X, MapDimensions.Y);
            map.cells = new Cell[map.MapDimensions.X, map.MapDimensions.Y];
            map.SourceCells = new List<SourceCell>();
            map.SinkCells = new List<SinkCell>();
            map.MirrorCells = new List<MirrorCell>();
            map.PrismCells = new List<PrismCell>();
            map.HueCells = new List<HueCell>();
            map.WallCells = new List<WallCell>();
            map.EmptyCells = new List<EmptyCell>();
            map.NullCells = new List<NullCell>();
            map.BulbCells = new List<BulbCell>();
            map.Par = Par;
            map.Seconds = Seconds;
            map.Skin = Skin.Clone();
            map.MapSkinName = MapSkinName;
            map.ShaderModel3 = ShaderModel3;
            foreach (Cell cell in cells)
            {
                Cell clone = cell.Clone() as Cell;
                clone.Map = map;
                map.SetCell(clone);
            }
            map.AmbientCueName = AmbientCueName;
            map.MusicCueName = MusicCueName;

            map.SwitchEffect = SwitchEffect;
            map.Fog = Fog;

            return map;
        }
        #endregion
    }
}
